﻿using AutoMapper;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using X23.Application.RoleManagement.Dto;
using X23.Application.UserManagement.Dto;
using X23.EFCore.Repository;
using X23.EFCore.Service;
using X23.Model.Const;
using X23.Model.Menu;
using X23.Model.Role;
using X23.Model.User;
using X23.Util.CacheUtil;
using X23.Util.SerializationUtil;

namespace X23.Application.RoleManagement
{
    public class RoleManagementService : CRUDService<RoleDto, Role>, IRoleManagementService
    {
        private readonly IMapper _mapper;
        private readonly ICacheService _cacheService;
        private readonly IRepository<Role> _roleRepository;
        private readonly IRepository<User> _userRepository;
        private readonly IRepository<UserRole> _userRoleRepository;
        private readonly IRepository<MenuButtonRole> _menuButtonRoleRepository;
        private readonly IRepository<Menu> _menuRepository;

        public RoleManagementService(
            IMapper mapper,
            ICacheService cacheService,
            IRepository<Role> roleRepository,
            IRepository<User> userRepository,
            IRepository<UserRole> userRoleRepository,
            IRepository<MenuButtonRole> menuButtonRoleRepository,
            IRepository<Menu> menuRepository
            ) : base(roleRepository, mapper)
        {
            _mapper = mapper;
            _cacheService = cacheService;
            _roleRepository = roleRepository;
            _userRepository = userRepository;
            _userRoleRepository = userRoleRepository;
            _menuButtonRoleRepository = menuButtonRoleRepository;
            _menuRepository = menuRepository;
        }

        /// <summary>
        /// 获取用户下的角色
        /// </summary>
        /// <param name="userId">用户Id</param>
        /// <returns></returns>
        public async Task<List<RoleDto>> GetRoleListByUserId(int userId)
        {
            var query = from userRole in (await _userRoleRepository.GetAllAsync()).Where(x => x.UserId == userId)
                        join role in (await _roleRepository.GetAllAsync())
                        on userRole.RoleId equals role.Id
                        select role;
            var result = query.OrderBy(x => x.Id).ToList();

            return await Task.FromResult(_mapper.Map<List<RoleDto>>(result));
        }


        public async Task AddOrUpdateRoleMenuTreeDto(RoleMenuTreeDto roleMenuTreeDto)
        {
            var roleId = roleMenuTreeDto.Id;
            if ((await _roleRepository.GetAllAsync()).Any(x => x.Name == roleMenuTreeDto.Name && x.Id != roleId))
            {
                throw new Exception("角色名称重复！");
            }
            if (roleId == 0)
            {
                //添加
                var role = new Role { Name = roleMenuTreeDto.Name };
                await _roleRepository.AddAsync(role);
                roleId = role.Id;
            }
            else
            {
                //修改
                var role = await _roleRepository.GetByIdAsync(roleId);
                if (role == null)
                {
                    throw new Exception("当前角色不存在！");
                }
                role.Name = roleMenuTreeDto.Name;
                await _roleRepository.UpdateAsync(role);

                //覆盖添加 删除按钮 再添加按钮
                await _menuButtonRoleRepository.DeleteListAsync(x => x.RoleId == roleId);
            }

            //添加按钮
            var result = new List<MenuButtonRole>();
            ParseMenuButtonTreeDtoList(result, roleMenuTreeDto.MenuButtonTreeDtoList, roleId);
            await _menuButtonRoleRepository.AddListAsync(result);

        }

        private void ParseMenuButtonTreeDtoList(List<MenuButtonRole> result, List<MenuButtonTreeDto> list,int roleId)
        {
            list.ForEach(item =>
            {
                item.ButtonCodeList.ForEach(itemButton =>
                {
                    result.Add(new MenuButtonRole
                    {
                        ButtonCode = itemButton,
                        Id = 0,
                        RoleId = roleId,
                        MenuId = item.Id,
                    });
                });
                if (item.Children.Count>0)
                {
                    ParseMenuButtonTreeDtoList(result, item.Children, roleId);
                }
            });
        }

        public async Task<RoleMenuTreeDto> GetRoleMenuTreeDto(int roleId)
        {
            var role = await _roleRepository.GetByIdAsync(roleId);
            var roleButton = (await _menuButtonRoleRepository.GetAllAsync()).Where(x => x.RoleId == roleId).ToList();
            var menulist = (await _menuRepository.GetAllAsync()).OrderBy(x => x.Seq).ToList();

            var firstList = menulist.Where(x => x.ParentId == 0).Select(x => new MenuButtonTreeDto
            {
                Children = new List<MenuButtonTreeDto>(),
                Id = x.Id,
                Name = x.Name,
                ButtonCodeList = roleButton.Where(y => y.MenuId == x.Id).Select(y => y.ButtonCode).ToList()
            }).ToList();

            RecursionTree(firstList, roleButton, menulist);

            return new RoleMenuTreeDto() { Id = roleId, Name = role?.Name, MenuButtonTreeDtoList = firstList };
        }

        private void RecursionTree(List<MenuButtonTreeDto> nodes, List<MenuButtonRole> menuButtonRoles, List<Menu> menus)
        {
            nodes.ForEach(item =>
            {
                item.Children = menus.Where(x => x.ParentId == item.Id).Select(x => new MenuButtonTreeDto
                {
                    Children = new List<MenuButtonTreeDto>(),
                    Id = x.Id,
                    Name = x.Name,
                    ButtonCodeList = menuButtonRoles.Where(y => y.MenuId == x.Id).Select(y => y.ButtonCode).ToList()
                }).ToList();
                if (item.Children.Count > 0)
                {
                    RecursionTree(item.Children, menuButtonRoles, menus);
                }
            });
        }


        /// <summary>
        /// 获取角色下的用户
        /// </summary>
        /// <param name="roleId">角色Id</param>
        /// <returns></returns>
        public async Task<List<UserDto>> GetUserListByRoleId(int roleId)
        {
            var query = from userRole in (await _userRoleRepository.GetAllAsync()).Where(x => x.RoleId == roleId)
                        join user in (await _userRepository.GetAllAsync())
                        on userRole.UserId equals user.Id
                        select user;
            var result = query.OrderBy(x => x.Id).ToList();

            return await Task.FromResult(_mapper.Map<List<UserDto>>(result));
        }

        /// <summary>
        /// 获取用户权限
        /// </summary>
        /// <param name="userId">用户Id</param>
        /// <returns></returns>
        public async Task<List<UserMenuButtonDto>> GetUserPermissionWithCache(int userId)
        {
            var result = await _cacheService.GetOrCreateAsync($"{CacheKey.UserPermissionKey}_{userId}", () => GetPermissionByUserId(userId));
            return result.ToObjet<List<UserMenuButtonDto>>();
        }
        private async Task<string> GetPermissionByUserId(int userId)
        {
            var query = from role in (await _userRoleRepository.GetAllAsync()).Where(x => x.UserId == userId)
                        join menuButtonRole in (await _menuButtonRoleRepository.GetAllAsync())
                        on role.RoleId equals menuButtonRole.RoleId
                        join menu in (await _menuRepository.GetAllAsync())
                        on menuButtonRole.MenuId equals menu.Id
                        select new UserMenuButtonDto
                        {
                            ButtonCode = menuButtonRole.ButtonCode,
                            ControllerName = menu.ControllerName,
                            UserId = userId
                        };
            var result = query.ToList().ToJson();

            return await Task.FromResult(result);
        }



        public async override Task<bool> DeleteAsync(object id, bool isPhysicalDelete = false)
        {
            var boolRole = await _roleRepository.DeleteAsync(id, isPhysicalDelete);
            var boolButton = await _menuButtonRoleRepository.DeleteListAsync(x => x.RoleId == (int)id, isPhysicalDelete);
            return boolRole && boolButton;
        }


        public async Task<ButtonPermissionDto> GetButtonPermissionWithCache(string url, int userId)
        {
            var result = await _cacheService.GetOrCreateAsync($"{CacheKey.UserButtonPermissionKey}_{url}_{userId}", () => GetButtonPermission(url, userId));
            return result.ToObjet<ButtonPermissionDto>();
        }
        private async Task<string> GetButtonPermission(string url, int userId)
        {
            var query = from userRole in (await _userRoleRepository.GetAllAsync()).Where(x => x.UserId == userId)
                        join role in (await _roleRepository.GetAllAsync())
                        on userRole.RoleId equals role.Id
                        join menuButtonRole in (await _menuButtonRoleRepository.GetAllAsync())
                        on userRole.RoleId equals menuButtonRole.RoleId
                        join menu in (await _menuRepository.GetAllAsync()).Where(x => x.Url == url)
                        on menuButtonRole.MenuId equals menu.Id
                        select new ButtonPermissionDto
                        {
                            MenuUrl = url,
                            HasQuery = menuButtonRole.ButtonCode == ButtonConst.Query,
                            HasAdd = menuButtonRole.ButtonCode == ButtonConst.Add,
                            HasUpdate = menuButtonRole.ButtonCode == ButtonConst.Update,
                            HasDelete = menuButtonRole.ButtonCode == ButtonConst.Delete,
                        };
            var result = new ButtonPermissionDto
            {
                MenuUrl = url,
                HasQuery = query.Any(x => x.HasQuery),
                HasAdd = query.Any(x => x.HasAdd),
                HasUpdate = query.Any(x => x.HasUpdate),
                HasDelete = query.Any(x => x.HasDelete),
            };
            return result.ToJson();
        }
    }
}
